/**
 * 
 */
package at.ac.tuwien.vpsbcm.spielzeugfabrik.space;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Serializable;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

import org.mozartspaces.capi3.CountNotMetException;
import org.mozartspaces.capi3.FifoCoordinator;
import org.mozartspaces.capi3.LindaCoordinator;
import org.mozartspaces.core.Capi;
import org.mozartspaces.core.ContainerReference;
import org.mozartspaces.core.DefaultMzsCore;
import org.mozartspaces.core.Entry;
import org.mozartspaces.core.MzsCoreException;
import org.mozartspaces.core.TransactionReference;
import org.mozartspaces.notifications.Notification;
import org.mozartspaces.notifications.NotificationListener;
import org.mozartspaces.notifications.NotificationManager;
import org.mozartspaces.notifications.Operation;

import at.ac.tuwien.vpsbcm.spielzeugfabrik.BauteilEnum;

/**
 * @author Andreas Wolf
 *
 */
public class Lackierer implements Runnable, NotificationListener {
	private volatile boolean goOn;
	private volatile boolean checkForNew; 
	private Integer ID;
	private String Color;
	private URI spaceurl;
	private DefaultMzsCore core;
	private Capi capi;
	private NotificationManager notifManager;
	
	private ContainerReference BauteilContainer;
	private ContainerReference LokomotivenContainer;
	
	private final int transDuration = 2000;
	
	public Lackierer (URI spaceurl, Integer ID, String Color) {
		this.ID = ID;
		this.Color = Color;
		this.goOn = true;
		this.checkForNew = true;
		try {
			System.out.format("start lackierer %d", ID);
			this.spaceurl = spaceurl;
			core = DefaultMzsCore.newInstanceWithoutSpace();
			capi = new Capi(core);
			BauteilContainer = capi.lookupContainer("Bauteile", spaceurl, 0, null);
			LokomotivenContainer = capi.lookupContainer("Lokomotiven", spaceurl, 0, null);
			notifManager = new NotificationManager(core);
			notifManager.createNotification(BauteilContainer, this, Operation.WRITE, Operation.DELETE, Operation.TAKE);
			notifManager.createNotification(LokomotivenContainer, this, Operation.WRITE, Operation.DELETE, Operation.TAKE);
			// start the work
		} catch (MzsCoreException e) {
			this.goOn = false;
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}		
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		TransactionReference tx = null;
		int partAnz = 0, lokAnz = 0;
		ArrayList<Bauteil> teilA;
		ArrayList<Lokomotive> lokA;

		while (this.goOn) {
			try {
				tx = capi.createTransaction(transDuration, spaceurl);
				System.out.println("start coloring of a part or train");
				// find out if uncolored trains or parts are available
				try {
					lokAnz = capi.test(LokomotivenContainer, FifoCoordinator.newSelector(1, "unlackierte"), 0, tx);
				} catch (CountNotMetException e) {
					lokAnz = 0;
				}
				try {
					partAnz = capi.test(BauteilContainer, FifoCoordinator.newSelector(1, "unlackierte"), 0, tx);
				} catch (CountNotMetException e) {
					partAnz = 0;
				}
				System.out.format("Loks: %d, Parts: %d\n", lokAnz, partAnz);
				if (lokAnz > 0) {
					// if uncolored trains are available, take one and color it!
					lokA = capi.take(LokomotivenContainer, FifoCoordinator.newSelector(1, "unlackierte"), 0, tx);
					colorizeLokomotive(lokA.get(0), tx);
					capi.commitTransaction(tx);
				} else if (partAnz > 0) {
					// if no uncolored trains, but uncolored parts are available, get one of those!
					teilA = capi.take(BauteilContainer, FifoCoordinator.newSelector(1, "unlackierte"), 0, tx);
					colorizeBauteil(teilA.get(0), tx);
					capi.commitTransaction(tx);
				} else {
					capi.rollbackTransaction(tx);
					// so, currently nothing is available - let's wait for new stuff.
					waitCheckForNew();
				}
				
				
			} catch (MzsCoreException e) {
				e.printStackTrace();
			}
			
		}
		notifManager.shutdown();
		core.shutdown(false);
	}

	private void colorizeBauteil(Bauteil teil, TransactionReference tx)
			throws MzsCoreException {
		Entry entry;
		String FifoName;
		if (teil.getBauteilTyp() == BauteilEnum.HOLZGEHAEUSE) {
			FifoName = "lackierte_gehaeuse";
		} else {
			FifoName = "lackierte_schornsteine";
		}
		teil.setLackiererID(ID);
		teil.setColor(Color);
		teil.setColored(true);
		entry = new Entry( teil
				         , FifoCoordinator.newCoordinationData(FifoName)
				         , LindaCoordinator.newCoordinationData()
				         );
		capi.write(entry, BauteilContainer, 0, tx);
		System.out.println("colorize bauteil!");
	}

	private void colorizeLokomotive(Lokomotive lok, TransactionReference tx)
			throws MzsCoreException {
		Entry entry;
		lok.setLackiererGehaeuseID(ID);
		lok.setLackiererSchornsteinID(ID);
		lok.setColor(Color);
		lok.setColored(true);
		entry = new Entry (lok, LindaCoordinator.newCoordinationData(), FifoCoordinator.newCoordinationData(), FifoCoordinator.newCoordinationData("fertige"));
		capi.write(entry, LokomotivenContainer, 0, tx);
		System.out.println("colorize lokomotive!");
	}
	public boolean isRunning () {
		return this.goOn;
	}
	
	public void stop () {
		this.goOn = false;
	}
	
	private synchronized void waitCheckForNew () {
		while (!this.checkForNew) {
			try {
				wait();
			} catch (InterruptedException e) {}
		}
		this.checkForNew = false;
	}
	
	public synchronized void setCheckForNew () {
		this.checkForNew = true;
		notifyAll();
	}
	
	public void entryOperationFinished(Notification source,
			Operation operation, List<? extends Serializable> entries) {
		setCheckForNew();
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		URI spaceurl;
		Lackierer lacki;
		int ID;
		int c;
		String Color;
		BufferedReader br = new BufferedReader (new InputStreamReader(System.in));
		if (args.length == 3) {
			try {
				spaceurl = new URI(args[0]);
				ID = Integer.parseInt(args[1]);
				Color = args[2];
				lacki = new Lackierer(spaceurl, ID, Color);					
				Thread t = new Thread(lacki);
				System.out.println("Starte Lackierer - Abbruch mit der Taste 'q'");
	            t.start();
	            c = br.read();
	            while (c != 'q' && c != 'Q' && lacki.isRunning()) {
	            	c = br.read();
	            }
	            lacki.stop();
	            lacki.setCheckForNew();
			} catch (URISyntaxException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		} else {
			System.out.println("Brauche Space-URI als Kommandozeilenparameter!");		
		}

	}
}
